package dragon.core.io;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import dragon.core.exception.ExceptionHandler;
import dragon.core.util.DateUtils;
import dragon.core.util.StringUtils;

/**
 * 文件处理器
 * 
 * @author 
 */
public class FileHandler {

	private static final Logger logger = LoggerFactory.getLogger(FileHandler.class);

	/**
	 * 文件路径分隔符
	 */
	public static String separator = System.getProperty("file.separator");

	/**
	 * 默认字符集编码
	 */
	public static final String defaultCharset = "UTF-8";

	/**
	 * 判断文件(夹)是否存在
	 * 
	 * @param filePath
	 *            文件(夹)路径
	 * @return
	 */
	public static boolean isFileExists(String filePath) {
		File file = new File(filePath);
		return file.exists();
	}

	/**
	 * 判断文件是否是目录
	 * 
	 * @param path
	 *            文件路径
	 * @return
	 */
	public static boolean isDirectory(String path) {
		File file = new File(path);
		return file.isDirectory();
	}

	/**
	 * 获取不带扩展名的文件名
	 * 
	 * @param fileName
	 *            文件名
	 * @return
	 */
	public static String getFileNameWithoutExtension(String fileName) {
		if (StringUtils.isEmpty(fileName)) {
			return "";
		}
		int index = fileName.lastIndexOf(".");
		if (index != -1) {
			return fileName.substring(0, index);
		} else {
			return fileName;
		}
	}

	/**
	 * 获取文件(以文件流的方式)
	 * 
	 * @param filePath
	 *            文件路径
	 * @return
	 */
	public static InputStream readFile(String filePath) throws ExceptionHandler {

		if (!isFileExists(filePath)) {
			throw new ExceptionHandler("目标文件不存在！");
		}

		if (isDirectory(filePath)) {
			throw new ExceptionHandler("目标文件是目录类型，无法读取内容！");
		}

		try {
			return new FileInputStream(new File(filePath));
		} catch (IOException e) {
			throw new ExceptionHandler(e.getMessage(), e);
		}
	}

	/**
	 * 获取文件内容
	 * 
	 * @param filePath
	 *            文件路径
	 * @param excludeEmptyLine
	 *            是否排除空内容行
	 * @param charsetName
	 *            字符集编码
	 * @return
	 */
	public static String readFile(String filePath, boolean excludeEmptyLine, String charsetName) throws ExceptionHandler {

		if (!isFileExists(filePath)) {
			throw new ExceptionHandler("目标文件不存在！");
		}

		if (isDirectory(filePath)) {
			throw new ExceptionHandler("目标文件是目录类型，无法读取内容！");
		}

		try {
			return readFile(new FileInputStream(new File(filePath)), excludeEmptyLine, charsetName);
		} catch (IOException e) {
			throw new ExceptionHandler(e.getMessage(), e);
		}
	}

	/**
	 * 获取文件内容
	 * 
	 * @param fileInputStream
	 *            文件流
	 * @param excludeEmptyLine
	 *            是否排除空内容行
	 * @param charsetName
	 *            字符集编码
	 * @return
	 * @throws ExceptionHandler
	 */
	public static String readFile(InputStream fileInputStream, boolean excludeEmptyLine, String charsetName) throws ExceptionHandler {

		StringBuilder sb = new StringBuilder();
		try {
			BufferedInputStream bis = new BufferedInputStream(fileInputStream);
			if (charsetName == null) {
				charsetName = defaultCharset;
			}
			InputStreamReader isr = new InputStreamReader(bis, charsetName);
			BufferedReader br = new BufferedReader(isr);
			String temp = null;
			while ((temp = br.readLine()) != null) {

				// 排除空内容的行
				if (excludeEmptyLine && StringUtils.isEmpty(temp)) {
					continue;
				}

				sb.append(temp).append("\n");
			}
			br.close();
			isr.close();
			bis.close();
		} catch (FileNotFoundException e) {
			throw new ExceptionHandler(e.getMessage(), e);
		} catch (IOException e) {
			throw new ExceptionHandler(e.getMessage(), e);
		}
		return sb.toString();
	}

	/**
	 * 读取系统配置文件
	 * 
	 * @param fileName
	 *            系统配置文件名称，该配置文件存在于src目录中
	 * @param charset
	 *            字符集编码
	 * @return
	 * @throws ExceptionHandler
	 */
	public static Map<String, String> getProperties(String fileName, String charset) throws ExceptionHandler {

		if (StringUtils.isEmpty(charset)) {
			charset = defaultCharset;
		}

		Map<String, String> config = new HashMap<String, String>();

		InputStream is = getResourceAsStream(fileName);
		if (is == null) {
			throw new ExceptionHandler("在class根目录下未找到属性文件：" + fileName);
		}

		String temp = null;
		try {
			InputStreamReader isr = new InputStreamReader(is, charset);
			BufferedReader br = new BufferedReader(isr);
			while ((temp = br.readLine()) != null) {
				String[] tmps = temp.split("=");
				if (tmps != null && tmps.length == 2) {
					config.put(tmps[0].trim(), tmps[1].trim());
				}
			}
			br.close();
		} catch (IOException e) {
			throw new ExceptionHandler(e);
		}

		return config;
	}

	/**
	 * 文件(夹)重命名
	 * 
	 * @param filePath
	 *            文件(夹)路径
	 * @param newFileName
	 *            新的文件(夹)名
	 */
	public static void renameFile(String filePath, String newFileName) {
		String newFilePath = filePath.substring(0, filePath.lastIndexOf(separator) + 1) + newFileName;
		File oldFile = new File(filePath);
		File newFile = new File(newFilePath);
		oldFile.renameTo(newFile);
	}

	/**
	 * 移动文件
	 * 
	 * @param source
	 *            源文件(夹)路径
	 * @param target
	 *            目标文件夹路径
	 * @throws ExceptionHandler
	 */
	public static void moveFile(String source, String target) throws ExceptionHandler {
		copyFile(source, target);
		deleteFiles(source, true);
	}

	/**
	 * 拷贝文件
	 * 
	 * @param source
	 *            源文件(夹)路径
	 * @param target
	 *            目标文件夹路径
	 */
	public static void copyFile(String source, String target) throws ExceptionHandler {
		if (!isFileExists(source)) {
			throw new ExceptionHandler("源文件不存在！");
		}

		if (!isDirectory(target)) {
			throw new ExceptionHandler("目标文件不是目录！");
		}

		// 避免将文件夹拷贝(移动)到其子文件夹中
		if (isDirectory(source)) {
			if (target.startsWith(source)) {
				throw new ExceptionHandler("不能将文件夹拷贝(移动)到其子文件夹中！");
			}
		}

		// 去除末尾多余的 \
		while (target.endsWith(separator)) {
			target = target.substring(0, target.length() - 1);
		}

		if (isDirectory(source)) {
			// 如果待拷贝的文件是目录，则需要连同其子目录和文件一同拷贝
			File folder = new File(source);
			// 在目标目录中创建待拷贝的目录
			String newFolderName = null;
			if (folder.getParent().equals(target)) {
				newFolderName = "复件 " + folder.getName();
			} else {
				newFolderName = folder.getName();
			}
			String newFolderPath = target + separator + newFolderName;
			mkDir(newFolderPath);
			File[] files = folder.listFiles();
			for (File f : files) {
				String _target = target + separator + newFolderName;
				copyFile(f.getAbsolutePath(), _target);
			}

		} else {
			File file = new File(source);
			String newFileName = null;
			if (file.getParent().equals(target)) {
				newFileName = "复件 " + file.getName();
			} else {
				newFileName = file.getName();
			}
			String targetFile = target + separator + newFileName;
			try {
				FileInputStream fis = new FileInputStream(file);
				createBinaryFile(fis, targetFile);
			} catch (FileNotFoundException e) {
				throw new ExceptionHandler(e.getMessage(), e);
			}
		}

	}

	/**
	 * 物理文件转换成逻辑文件
	 * 
	 * @param f
	 *            物理文件
	 * @param lf
	 *            逻辑文件
	 * @throws ExceptionHandler
	 */
	private static void file2LogicFile(File f, LogicFile lf) throws ExceptionHandler {
		lf.setName(f.getName());
		lf.setSize(Long.valueOf(f.length()).intValue());
		lf.setSizeL(f.length());
		//lf.setCreateTime(GetFileCreateTime.getCreationTime(f.getAbsolutePath()));
		lf.setLastModifyTime(DateUtils.getDateTimeStr_(f.lastModified()));
		lf.setLastModifyTimeL(f.lastModified());
		lf.setFolder(f.isDirectory());
		lf.setPath(f.getAbsolutePath());
	}

	/**
	 * 根据给定的路径返回其下所有文件
	 * 
	 * @param folderPath
	 *            文件系统入口路径 <code>java.lang.String</code>
	 * @param type
	 *            获取种类 0 包含文件和文件夹，1文件，2文件夹
	 * @param recursive
	 *            是否递归处理子目录 <code>true</code>递归处理 <code>false</code> 不递归处理
	 * @return List<LogicFile> 返回指定文件入口下的所有文件(逻辑文件) <code>java.util.List<LogicFile></code>
	 * @throws ExceptionHandler
	 */
	@SuppressWarnings("unchecked")
	public static List<LogicFile> getLogicFiles(String folderPath, int type, boolean recursive) throws ExceptionHandler {
		List<File> files = getFiles(folderPath, type, recursive);
		List<LogicFile> result = new ArrayList<LogicFile>();
		for (File f : files) {
			LogicFile lf = new LogicFile();
			file2LogicFile(f, lf);
			result.add(lf);
		}
		Collections.sort(result, new LogicFileSort());
		return result;
	}

	/**
	 * 根据给定的路径返回其下所有文件
	 * 
	 * @param folderPath
	 *            文件系统入口路径 <code>java.lang.String</code>
	 * @return List<LogicFile> 返回指定文件入口下的所有文件(逻辑文件) <code>java.util.List<LogicFile></code>
	 * @throws ExceptionHandler
	 */
	public static List<LogicFile> getLogicFilesHasTreeData(String folderPath) throws ExceptionHandler {
		List<File> files = getFiles(folderPath, 0, true);
		File file = new File(folderPath);
		files.add(file);
		List<LogicFile> result = new ArrayList<LogicFile>();
		for (File f : files) {
			LogicFile lf = new LogicFile();
			file2LogicFile(f, lf);
			result.add(lf);
		}
		FileManager fm = new FileManagerImp();
		return fm.Path2Tree(result);
	}

	/**
	 * 根据给定的路径返回其下所有文件
	 * 
	 * @param folderPath
	 *            文件系统入口路径 <code>java.lang.String</code>
	 * @param type
	 *            获取种类 0 包含文件和文件夹，1文件，2文件夹
	 * @param recursive
	 *            是否递归处理子目录 <code>true</code>递归处理 <code>false</code> 不递归处理
	 * @return List<File> 返回指定文件入口下的所有文件 <code>java.util.List</code>
	 */
	public static List<File> getFiles(String folderPath, int type, boolean recursive) {
		List<File> files = new ArrayList<File>();
		File file = new File(folderPath);
		if (file.exists()) {
			if (file.isDirectory()) {
				for (File subfile : file.listFiles()) {
					if (subfile.isDirectory()) {
						if (type == 0 || type == 2) {
							files.add(subfile);// 加入目录
						}
						if (recursive) {
							List<File> subfiles = getFiles(subfile.getAbsolutePath(), type, recursive);
							files.addAll(subfiles);
						}
					} else {
						if (type == 0 || type == 1) {
							if (!isSpecialFile(subfile)) {
								files.add(subfile);// 加入文件
							}
						}
					}
				}
			} else {
				if (type == 0 || type == 1) {
					if (!isSpecialFile(file)) {
						files.add(file);// 加入文件
					}
				}
			}
		}
		return files;
	}

	/**
	 * 检测文件是否特殊文件
	 * 
	 * @param file
	 *            待检测文件
	 * @return
	 */
	private static boolean isSpecialFile(File file) {
		if (file.isFile()) {
			if (file.getName().equals("Thumbs.db")) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 传入路径 创建目录
	 * 
	 * @param path
	 *            <code>java.lang.String</code>
	 */
	public static void mkDir(String path) {

		File dir = new File(path);

		dir.mkdirs();
	}

	/**
	 * 创建文件
	 * 
	 * @param filePath
	 *            路径
	 * @param content
	 *            内容
	 * @param charsetName
	 *            编码，默认为平台默认编码
	 * @throws ExceptionHandler
	 */
	public static void createPlainTextFile(String filePath, String content, String charsetName) throws ExceptionHandler {

		File file = new File(filePath);
		// 如果文件夹不存在，先创建文件夹
		if (!file.getParentFile().exists()) {
			mkDir(file.getParentFile().getAbsolutePath());
		}
		try {
			// 默认字符集
			if (charsetName == null) {
				charsetName = defaultCharset;
			}

			if (content == null) {
				content = "";
			}

			BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), charsetName));
			bw.write(content);
			bw.flush();
			bw.close();
		} catch (IOException e) {
			throw new ExceptionHandler(e.getMessage(), e);
		}
	}

	/**
	 * 创建文件
	 * 
	 * @param ins
	 *            文件输入流
	 * @param filePath
	 *            文件写入地址
	 * @return
	 * @throws ExceptionHandler
	 */
	public static boolean createBinaryFile(InputStream ins, String filePath) throws ExceptionHandler {

		BufferedInputStream bis = null;
		BufferedOutputStream bos = null;
		try {
			File file = new File(filePath);

			if (!file.exists()) {
				mkDir(file.getParent());
				if (!file.createNewFile()) {
					return false;
				}
			}

			bis = new BufferedInputStream(ins);

			OutputStream os = new FileOutputStream(file);
			bos = new BufferedOutputStream(os);

			byte[] b = new byte[512];

			int length = -1;
			while ((length = bis.read(b)) != -1) {
				bos.write(b, 0, length);
			}

			bos.flush();
			return true;
		} catch (Exception e) {
			throw new ExceptionHandler(e);
		} finally {
			if (bos != null) {
				try {
					bos.close();
				} catch (IOException e) {
					logger.error(e.getMessage(), e);
				}
				bos = null;
			}

			if (bis != null) {
				try {
					bis.close();
				} catch (IOException e) {
					logger.error(e.getMessage(), e);
				}
				bis = null;
			}
		}
	}

	/**
	 * 追加文件内容
	 * 
	 * @param filePath
	 *            文件路径
	 * @param content
	 *            文件内容
	 * @throws ExceptionHandler
	 */
	public static void appendFile(String filePath, String content) throws ExceptionHandler {
		appendFile(filePath, new String[] { content });
	}

	/**
	 * 追加文件内容
	 * 
	 * @param filePath
	 *            文件路径
	 * @param contents
	 *            文件内容
	 * @throws ExceptionHandler
	 */
	public static void appendFile(String filePath, String[] contents) throws ExceptionHandler {

		File file = new File(filePath);
		if (!file.exists()) {
			createPlainTextFile(filePath, null, null);
		}
		try {
			FileWriter fw = new FileWriter(filePath, true);
			BufferedWriter bw = new BufferedWriter(fw);
			for (String s : contents) {
				bw.newLine();
				bw.append(s);
			}

			bw.flush();
			bw.close();
		} catch (IOException e) {
			throw new ExceptionHandler(e.getMessage(), e);
		}

	}

	/**
	 * 移除指定路径下的文件(夹)
	 * 
	 * @param filePath
	 *            文件路径
	 * @param includeItself
	 *            是否删除自身，如果传入的路径是文件夹，且 includeItself 为true，则连同此文件夹一并删除；如果传入的是文件，此参数没有意义
	 */
	public static void deleteFiles(String filePath, boolean includeItself) {
		File tmpFile = new File(filePath);
		if (tmpFile.exists()) {
			if (tmpFile.isDirectory()) {
				File[] files = tmpFile.listFiles();
				for (File file : files) {
					deleteFiles(file.getAbsolutePath(), includeItself);// 迭代删除文件
					if (file.isDirectory()) {
						file.delete();// 删除子文件夹
					}
				}
				if (includeItself) {
					tmpFile.delete();
				}
			} else {
				tmpFile.setWritable(true);
				tmpFile.delete();// 删除文件
			}
		}
	}

	/**
	 * 将对象序列化到文件
	 * 
	 * @param obj
	 *            对象
	 * @param filePath
	 *            文件路径
	 * @return
	 * @throws ExceptionHandler
	 */
	public static boolean serializeObject(Object obj, String filePath) throws ExceptionHandler {
		try {
			File file = new File(filePath);
			if (!file.exists()) {
				mkDir(file.getParent());
				if (!file.createNewFile()) {
					return false;
				}
			}

			FileOutputStream fos = new FileOutputStream(filePath);
			ObjectOutputStream oos = new ObjectOutputStream(fos);
			oos.writeObject(obj);

			oos.flush();
			oos.close();
			fos.close();

			return true;
		} catch (Exception e) {
			throw new ExceptionHandler(e);
		}
	}

	/**
	 * 从文件中反序列化对象
	 * 
	 * @param filePath
	 *            文件路径
	 * @return
	 * @throws ExceptionHandler
	 */
	public static Object deserializeObject(String filePath) throws ExceptionHandler {
		try {
			Object obj = new Object();
			File file = new File(filePath);
			if (!file.exists()) {
				return null;
			}

			FileInputStream fis = new FileInputStream(filePath);
			ObjectInputStream ois = new ObjectInputStream(fis);
			obj = ois.readObject();

			ois.close();
			fis.close();

			return obj;
		} catch (Exception e) {
			throw new ExceptionHandler(e);
		}
	}

	/**
	 * 将对象序列化到xml文件
	 * 
	 * @param obj
	 *            待序列化文件
	 * @param path
	 *            文件路径
	 * @throws ExceptionHandler
	 */
	public static void toXML(Object obj, String path) throws ExceptionHandler {
		OutputStream os = null;
		try {
			os = new FileOutputStream(path);
			XStream xs = new XStream(new DomDriver());
			xs.toXML(obj, os);
		} catch (FileNotFoundException e) {
			throw new ExceptionHandler(e);
		} finally {
			if (os != null) {
				try {
					os.close();
				} catch (IOException e) {
					logger.error(e.getMessage(), e);
				}
				os = null;
			}
		}
	}

	/**
	 * 从xml文件中反序列化对象
	 * 
	 * @param filePath
	 *            文件路径
	 * @return 路径表示的文件不存在时，返回null
	 */
	public static Object fromXML(String filePath) {

		File file = new File(filePath);
		if (!file.exists()) {
			return null;
		}

		XStream xs = new XStream(new DomDriver());
		return xs.fromXML(file);
	}

	/**
	 * 通过类加载器读取配置文件，默认文件存放路径:src根目录
	 * 
	 * @param fileName
	 *            文件名
	 * @return
	 */
	public static InputStream getResourceAsStream(String fileName) {
		InputStream in = FileHandler.class.getClassLoader().getResourceAsStream(fileName);
		return in;
	}

	/**
	 * 获取类的根路径
	 * 
	 * @return
	 */
	public static String getClassRootPath() {
		String path = FileHandler.class.getClassLoader().getResource("").toString();

		return path.substring("file:/".length());
	}

}
